package view;

import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JLabel;
import java.awt.Font;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JButton;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;

import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;


import model.HCalcResults;

public class MainScreen extends JPanel{
	private JTextField tf_x;
	private JTextField tf_N;
	private JTextField tf_n;
	private JTextField tf_k;
	
	private JButton btnCalculate;
	private JButton btnSimulate_n;
	private JButton btnSimulate_k;
	
	private JTable table;
	private TableModel resultsTableModel;
	private ChartPanel chartPanel;
	
	private NumberFormat decimalFormat = new DecimalFormat("0.########");
	
	public static final int CALCULATE_MODE = 0;
	public static final int SIMULATE_n_MODE = 1;
	public static final int SIMULATE_k_MODE = 2;
	
	public MainScreen() {
		setLayout(null);
		
		JLabel lblX = new JLabel("x:");
		lblX.setFont(new Font("Tahoma", Font.PLAIN, 14));
		lblX.setBounds(173, 74, 46, 14);
		add(lblX);
		
		JLabel lblN = new JLabel("N:");
		lblN.setFont(new Font("Tahoma", Font.PLAIN, 14));
		lblN.setBounds(173, 118, 46, 14);
		add(lblN);
		
		JLabel lblN_1 = new JLabel("n:");
		lblN_1.setFont(new Font("Tahoma", Font.PLAIN, 14));
		lblN_1.setBounds(303, 75, 46, 14);
		add(lblN_1);
		
		JLabel lblK = new JLabel("k:");
		lblK.setFont(new Font("Tahoma", Font.PLAIN, 14));
		lblK.setBounds(303, 118, 46, 14);
		add(lblK);
		
		JLabel lblHypergeometricCalculator = new JLabel("Hypergeometric Distribution Calculator");
		lblHypergeometricCalculator.setFont(new Font("Tahoma", Font.BOLD, 20));
		lblHypergeometricCalculator.setBounds(197, 11, 404, 52);
		add(lblHypergeometricCalculator);
		
		tf_x = new JTextField();
		tf_x.setBounds(197, 74, 86, 20);
		add(tf_x);
		tf_x.setColumns(10);
		
		tf_N = new JTextField();
		tf_N.setBounds(197, 117, 86, 20);
		add(tf_N);
		tf_N.setColumns(10);
		
		tf_n = new JTextField();
		tf_n.setBounds(323, 74, 86, 20);
		add(tf_n);
		tf_n.setColumns(10);
		
		tf_k = new JTextField();
		tf_k.setBounds(323, 117, 86, 20);
		add(tf_k);
		tf_k.setColumns(10);
		
		btnCalculate = new JButton("Calculate");
		btnCalculate.setBounds(441, 73, 101, 23);
		add(btnCalculate);
		
		btnSimulate_n = new JButton("Simulate n");
		btnSimulate_n.setBounds(441, 116, 101, 23);
		add(btnSimulate_n);
		
		btnSimulate_k = new JButton("Simulate k");
		btnSimulate_k.setBounds(552, 116, 101, 23);
		add(btnSimulate_k);
		
		JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
		tabbedPane.setBounds(64, 150, 669, 408);
		add(tabbedPane);
		
		JScrollPane tableScrollPane = new JScrollPane();
		tabbedPane.addTab("Table", null, tableScrollPane, null);
		
		table = new JTable();
		tableScrollPane.setViewportView(table);
		
		chartPanel = new ChartPanel(null);
		tabbedPane.addTab("Graph", null, chartPanel, null);
	}

	/* ErrorChecking Methods */
	public boolean isInputValid(int mode){
		
		
		
		if(!areAllFieldsFilled(mode))
			return false;
		
		if(!areAllFieldsPositiveIntegers(mode))
			return false;
		
		if(!areConstraintsSatisfied(mode))
			return false;
		
		
		return true;
		
	}
	
	public String getErrorMsg(int mode){
		
		String error = "";
		
		if(!areAllFieldsFilled(mode))
			error += "Please don't leave any fields blank.\n";
		
		if(!areAllFieldsPositiveIntegers(mode))
			error += "All numbers should be non-negative integers.\n";
		
		if(!areConstraintsSatisfied(mode))
			error += "Please satisfy: n <= N, k <= N, x <= n";
		
		return error;
	}
	
	private boolean areConstraintsSatisfied(int mode){
		Point2D.Double point = get_XRange();
		try{
			int N = get_N();
			int n = -1;
			int k = -1;
			
			if(mode!= SIMULATE_n_MODE)
				n = get_n();
			if(mode!= SIMULATE_k_MODE)
				k = get_k();
			
			if(mode!=SIMULATE_n_MODE && n > N)  //allow if simulating n
				return false;
			
			if(mode!=SIMULATE_n_MODE && point.getY() > n)//allow if simulating n
				return false;
			
			if(mode!=SIMULATE_k_MODE && k > N)//allow if simulating k
				return false;
			
			if(mode!= SIMULATE_k_MODE && point.getY() > k) //allow if simulating k
				return false;
		
		}catch(Exception e){return false;}
		
		return true;
	}
	
	private boolean areAllFieldsFilled(int mode){
		String x = tf_x.getText().trim();
		String n = tf_n.getText().trim();
		String N = tf_N.getText().trim();
		String k = tf_k.getText().trim();
		
		if(mode == CALCULATE_MODE){
			if(x.isEmpty() || n.isEmpty() || N.isEmpty() || k.isEmpty())
				return false;
		}
		else if(mode == SIMULATE_n_MODE){
			if(x.isEmpty() || N.isEmpty() || k.isEmpty())
				return false;
		}
		else if(mode == SIMULATE_k_MODE){
			if(x.isEmpty() || n.isEmpty() || N.isEmpty())
				return false;
		}
		
		return true;
	}
	
	private boolean areAllFieldsPositiveIntegers(int mode){
	
		try{
			if(get_XRange() == null)
				return false;
			
			
			if(mode == CALCULATE_MODE){
				int N = get_N();
				int n = get_n();
				int k = get_k();
				if(n <0 || N < 0 || k < 0)
					return false;
			}
			else if(mode == SIMULATE_n_MODE){
				int N = get_N();
				int k = get_k();
				if(N < 0 || k < 0)
					return false;
			}
			else if(mode == SIMULATE_k_MODE){
				int N = get_N();
				int n = get_n();
				if(N < 0 || n < 0)
					return false;
			}
			
		}
		catch(Exception e){
			return false;
		}
		
		return true;
	}
	
	/* Getters */
	public Point2D.Double get_XRange(){	
			String input = tf_x.getText();
			
			int lower_X;
			int upper_X;
			
			try{
				if(input.matches("\\d+")){
					lower_X = Integer.parseInt(input);
					upper_X = lower_X;
				}
				else if(input.matches("\\d+<=[x|X]<=\\d+")){
					StringTokenizer tokenizer = new StringTokenizer(input, "<=");		
					lower_X = Integer.parseInt(tokenizer.nextToken());
					tokenizer.nextToken();
					upper_X = Integer.parseInt(tokenizer.nextToken());	
				}
				else if(input.matches("\\d+>=[x|X]>=\\d+")){
					StringTokenizer tokenizer = new StringTokenizer(input, ">=");
					upper_X = Integer.parseInt(tokenizer.nextToken());
					tokenizer.nextToken();
					lower_X = Integer.parseInt(tokenizer.nextToken());
				}
				else if(input.matches("[x|X]<=\\d+")){
					lower_X = 0;
					StringTokenizer tokenizer = new StringTokenizer(input, "<=");
					tokenizer.nextToken();
					upper_X = Integer.parseInt(tokenizer.nextToken());
				}
				else if(input.matches("[x|X]>=\\d+")){
					StringTokenizer tokenizer = new StringTokenizer(input, ">=");
					tokenizer.nextToken();
					lower_X = Integer.parseInt(tokenizer.nextToken());
					upper_X = get_n();
				}
				else
					return null;
				}
			catch(Exception e){
				return null;
			}
			
			if(lower_X <= upper_X)
				return new Point2D.Double(lower_X, upper_X);
			else
				return null;
		}
	
	public int get_N() throws Exception{
		return Integer.parseInt(tf_N.getText());
	}
	
	public int get_n() throws Exception{
		return Integer.parseInt(tf_n.getText());
	}
	
	public int get_k() throws Exception{
		return Integer.parseInt(tf_k.getText());
	}
	
	/* Setters */
	public void setTableContent(HCalcResults results){
		//update table view here
		
		//construct colNames
		Vector<String> colNames = new Vector<String>();
		colNames.add(HCalcResults.x_LABEL);
		colNames.add(HCalcResults.h_LABEL);
		
		//construct the entries
		resultsTableModel = new DefaultTableModel(colNames, results.getResults().size()+1);
		
		int numResults = results.getResults().size();
		
		for(int i=0;i<numResults;i++){
			Point2D.Double curr = results.getResults().get(i);
			resultsTableModel.setValueAt(curr.getX(), i, 0);
			resultsTableModel.setValueAt(decimalFormat.format(curr.getY()), i, 1);
		}
		
		//Insert total as the last row entry
		resultsTableModel.setValueAt("TOTAL", numResults, 0);
		resultsTableModel.setValueAt(results.getTotalProb(), numResults, 1);
		table.setModel(resultsTableModel);
		
		table.revalidate();
	}
	
	public void setGraphContent(JFreeChart chart){
		 chartPanel.setChart(chart);
	}
			
	public void setCalculateBtnListener(ActionListener listener){
		setButtonListener(btnCalculate, listener);
	}
	
	public void displayError(String error){
		JOptionPane.showMessageDialog(null, error, "Error!", JOptionPane.WARNING_MESSAGE);
	}
	
	public void setSimulate_n_Listener(ActionListener listener){
		setButtonListener(btnSimulate_n, listener);
	}
	public void setSimulate_k_Listener(ActionListener listener){
		setButtonListener(btnSimulate_k, listener);
	}
	private void setButtonListener(JButton button, ActionListener listener){
		if(button.getActionListeners().length > 0)
			button.removeActionListener(button.getActionListeners()[0]);
		
		button.addActionListener(listener);
	}
}
